#include "VoiceChat.h"

VoiceChat::VoiceChat(uint16_t port) : send_buf(new char[WBuffLen]),
                                      recv_buf(new char[RBuffLen]),
                                      fromAddrLength(sizeof(from)),
                                      socket_fd(-1)
{
    sockaddr_in hostAddr;
    hostAddr.sin_family = AF_INET;
    hostAddr.sin_port = htons(port);
    hostAddr.sin_addr.s_addr = INADDR_ANY;
#ifdef _WIN32
    WSAData wsadata;
    if (WSAStartup(MAKEWORD(2, 2), &wsadata) == -1)
        perror("ERROE ON WSAStartup!\n");
#endif
    socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socket_fd == -1)
        perror("ERROE ON CREATEING SOCKET!\n");
    int err = bind(socket_fd, (sockaddr *)&hostAddr, sizeof(hostAddr));
    if (err == -1)
        perror("ERROE ON BINDING SOCKET!\n");

        //non block mode
#ifndef _WIN32
    fcntl(socket_fd, F_SETFL, (fcntl(socket_fd, F_GETFL) | O_NONBLOCK));
#else
    u_long io = 1;
    ioctlsocket(socket_fd, FIONBIO, &io);
#endif
}

VoiceChat::~VoiceChat()
{
    for (auto iter = talkers.begin(); iter != talkers.end(); iter++)
    {
        delete (sockaddr *)iter->second->Addr;
        delete iter->second;
    }

    delete[] send_buf;
    delete[] recv_buf;
}

void VoiceChat::Run()
{
    int receivedSize = recvfrom(socket_fd, recv_buf, RBuffLen, 0, &from, &fromAddrLength);
    if (receivedSize > 0)
    {
        uint8_t msg_t;
        char *ptr = recv_buf;
        ptr = VoipProtocol::DeserializeMSGType(recv_buf, msg_t);

        switch (msg_t)
        {
        case Entry_MSG:
            OnEntry(ptr, &from);
            break;
        case TalkMdiy_MSG:
            Talk_Modify(ptr, &from);
            break;
        case Audio_MSG:
            Broadcast(ptr, (receivedSize - (ptr - recv_buf)), &from);
            break;
        default:
            break;
        }
    }
    else
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
}

void VoiceChat::OnEntry(char *playload, sockaddr *from)
{
    uint32_t hash = HashCode(from);

    auto talker_in = talkers.find(hash);
    if (talker_in != talkers.end())
        return;

    sockaddr *nFrom = new sockaddr;
    memcpy(nFrom, from, fromAddrLength);
    talkers[hash] = new Talker{nFrom, 0};
}

void VoiceChat::Talk_Modify(char *playload, sockaddr *from)
{
    uint8_t flag = 0;
    uint32_t hash = HashCode(from);
    VoipProtocol::DeserializeNoTlKMDify(playload, flag);

    auto talker_ = talkers.find(hash);
    if (talker_ != talkers.end())
        talker_->second->flag = talker_->second->flag | flag;
}

void VoiceChat::Broadcast(char *playload, int playload_size, sockaddr *from)
{
    uint32_t self_hash = HashCode(from);

    for (auto iter = talkers.begin(); iter != talkers.end(); iter++)
    {
        if (iter->second->flag & NoTalk_MDIY || self_hash == iter->first)
            continue;

        sendto(socket_fd, playload, playload_size, 0, (sockaddr *)iter->second->Addr, fromAddrLength);
    }
}

uint32_t VoiceChat::HashCode(sockaddr *addr)
{
    return ((sockaddr_in *)addr)->sin_addr.s_addr / ((sockaddr_in *)addr)->sin_port;
}